<!-- 两个小球相互弹动案例 -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>两个小球相互弹动案例</title>
    <style>
        canvas {
            background-color: black;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script src="../utils.js"></script>
<script>
    // 小球绘制
    class Ball {
        constructor(options) {
            this.x = 0;
            this.y = 0;
            this.r = 200;
            this.fillStyle = 'blue';
            this.strokeStyle = 'black';
            // 小球缩放
            this.scaleX = 1;
            this.scaleY = 1;
            // 定义小球移动速度
            this.vx = 0;
            this.vy = 0;
            // 小球透明度
            this.alpha = 1;
            Object.assign(this, options);
            return this;
        }
        render(ctx) {
            let { x, y, r, fillStyle, strokeStyle, scaleX, scaleY, alpha } = this;
            // 保存绘图环境
            ctx.save();
            // 开始绘制
            // ctx.beginPath();
            ctx.beginPath();
            ctx.translate(x, y);
            ctx.scale(scaleX, scaleY);
            ctx.strokeStyle = strokeStyle;
            ctx.fillStyle = fillStyle;

            ctx.globalAlpha = alpha;

            ctx.arc(0, 0, r, 0, Math.PI * 2);


            ctx.fill();
            ctx.stroke();
            ctx.restore();
            ctx.closePath();
            return this;
        }
        // 判断点是否在图形内部
        isInside(x, y) {
            let dis = Math.sqrt(Math.pow(x - this.x, 2) + Math.pow(y - this.y, 2))
            return dis < this.r;
        }
    }
    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    let w = canvas.width = 500;
    let h = canvas.height = 500;
    let ball1 = new Ball({
        x: c.rand([0, w]),
        y: c.rand([0, h]),
        r: 20,
    });
    let ball2 = new Ball({
        x: c.rand([0, w]),
        y: c.rand([0, h]),
        r: 20,
    });
    // 用于标记小球是否拖动状态 
    let ball1_draging = ball2_draging = false;

    // 监听鼠标按下事件，判断是否单击了小球
    canvas.addEventListener('mousedown', (e) => {
        if (ball1.isInside(e.offsetX, e.offsetY)) {
            ball1_draging = true;
        }
        if (ball2.isInside(e.offsetX, e.offsetY)) {
            ball2_draging = true;
        }
        // 鼠标移动
        canvas.addEventListener('mousemove', (e) => {
            if (ball1_draging) {
                ball1.x = e.offsetX;
                ball1.y = e.offsetY;
                // ball1.render(ctx);
            }
            if (ball2_draging) {
                ball2.x = e.offsetX;
                ball2.y = e.offsetY;
            }
        });

        canvas.addEventListener('mouseup', (e) => {
            ball1_draging = ball2_draging = false;
        });
    });

    // 摩擦力
    let friction = 0.9;
    // 弹动系数
    let gravity = 0.03;
    // 两点的间距
    let distance = 200;

    /**
     * 小球之间弹动
     * @param {Object} source 发生弹动的小球 
     * @param {Object} target 目标小球
     */
    function awardTo(source, target) {
        // 计算小球之间的距离
        let dx = target.x - source.x;
        let dy = target.y - source.y;

        let angle = Math.atan2(dy, dx); // 计算两点之间的角度

        // 距离目标点越近移动速度越慢，vy vx为累加是因为，小球会移动到目标点另一侧，变成负值
        source.vx += (target.x - distance * Math.cos(angle) - source.x) * gravity;
        source.vy += (target.y - distance * Math.sin(angle) - source.y) * gravity;
        source.vx *= friction;
        source.vy *= friction;
        source.x += source.vx;
        source.y += source.vy;
    }

    (function move() {
        window.requestAnimationFrame(move);
        ctx.clearRect(0, 0, w, h);
        // 没有拖动小球1，小球1向小球2弹动
        if (!ball1_draging) {
            awardTo(ball1, ball2);
        }
        if (!ball2_draging) {
            awardTo(ball2, ball1);
        }


        ctx.beginPath();
        ctx.strokeWidth = 3;
        ctx.strokeStyle = '#fff';
        ctx.lineTo(ball1.x, ball1.y);
        ctx.lineTo(ball2.x, ball2.y);
        ctx.closePath();
        ctx.stroke();


        ball1.render(ctx);
        ball2.render(ctx);
    })()

</script>

</html>